<?php
final class SiteMap {
  private static $xmlObject = NULL;

  /** @var SimpleXMLElement */
  private static $currentNode = NULL;
  private static $defaultNode = NULL;
  private static $rootNode = NULL;

  private static $depth = -1;
  private static $prevDepth = -1;
  private static $depthCounter = 0;
  private static $renderChildRoots = true;

  public static function getCurrentNode()
  {
    return self::$currentNode;
  }

  /**
   * Load the SiteMap XML file
   * @return void
   */
  public static function init()
  {
    $xmlContent =  file_get_contents(Config::$siteMapXML);
    $xmlContent = Parser::parseData($xmlContent);
    self::$xmlObject = XML::loadString($xmlContent);

    $controller = URI::getSegment(0);
    $function = URI::getSegment(1);

    if($controller == '')
      $controller = Config::$defaultController;

    if($function == '')
      $function = Config::$defaultFunction;

    $params = URI::getParamsAsURI();

    self::$currentNode = self::getNode($controller, $function, $params);

    if(self::$currentNode == NULL)
      self::$currentNode = self::getNodeByVirtualPath(URI::getSegments());



    self::$defaultNode = self::getNode( Config::$defaultController, Config::$defaultFunction );
    self::$rootNode = self::getNode( 'root', 'root' );
  }

  /**
   * Returns the Root Node
   * @return SimpleXMLElement
   */
  public static function getRootNode()
  {
    return self::$rootNode;
  }

  /**
   * Returns All Nodes as an Array of SimpleXMLElement
   * @return Array
   */
  public static function getAllNodes()
  {
    return XML::getNodesByXPath(self::$xmlObject, '//*');
  }

  /**
   * Returns and array of arrays containing the searchString, url, pageTitle, metaDesc, controller and function
   * for each node that was found to contain the giving search string
   * @return Array
   */
  public static function searchAllNodesForString($searchString)
  {
    $nodes = self::getAllNodes();
    $found = array();

    foreach ($nodes as $node) {
      $isInCurrentNode = false;
      foreach($node->attributes() as $attr)
      {
        if(stristr((string)$attr, $searchString))
        {
          $found[] = array( 'searchString'=>$searchString, 'url'=>self::getURL($node), 'pageTitle'=>(string)self::getAttr($node, 'pageTitle'), 'metaDesc'=>(string)self::getAttr($node, 'metaDesc'), 'controller'=>(string)self::getAttr($node, 'controller'), 'function'=>(string)self::getAttr($node, 'function') ) ;
          $isInCurrentNode = true;
          break;
        }
      }
      if(!$isInCurrentNode)
      {
        $content = Master::getCurrentController()->loadMainContent((string)$node['function']);

        if(stristr($content, $searchString))
          $found[] = array( 'searchString'=>$searchString, 'url'=>self::getURL($node), 'pageTitle'=>(string)self::getAttr($node, 'pageTitle'), 'metaDesc'=>(string)self::getAttr($node, 'metaDesc'), 'controller'=>(string)self::getAttr($node, 'controller'), 'function'=>(string)self::getAttr($node, 'function') ) ;
      }
    }

    return $found;
  }

  private static function _getNodeByVirtualPath(array $uriSegments)
  {
    unset($uriSegments['query']);
    $xpathPattern = '//*';

    $counter = 0;
    foreach($uriSegments as $segment) {
      $xpathPattern .= "[@sefTitle='".strtolower($segment)."']";

      if($counter == count($uriSegments)-1)
        break;

      $xpathPattern .='//*';
      $counter++;
    }

    $node = XML::getNodesByXPath(self::$xmlObject, $xpathPattern);
    return isset($node[0])? $node[0]: NULL;
  }

  /**
   * Returns a SimpleXMLElement Object if the VirtualPath was found or NULL if it was not
   * @return SimpleXMLElement
   */
  public static function getNodeByVirtualPath(array $uriSegments)
  {
    $node = self::_getNodeByVirtualPath($uriSegments, Config::$siteLanguage);

    return $node;
  }

  /**
   * Returns the node based on parameter values
   * @return SimpleXMLElement
   */
  public static function getNode($controllerName, $functionName, $params = '')
  {
    $currentNode = NULL;

    if($params != '')
      $currentNode = XML::getNodeByAttributes(self::$xmlObject, array( 'controller'=>$controllerName, 'function'=>$functionName, 'params'=>$params ));
    else
      $currentNode = XML::getNodeByAttributes(self::$xmlObject, array( 'controller'=>$controllerName, 'function'=>$functionName));

    if(!isset($currentNode[0]))
      return NULL;

    return $currentNode[0];
  }

  /**
   * Returns the parent node of the passed in SimpleXMLElement node
   * @return SimpleXMLElement
   */
  public static function getParentNode( $node )
  {
    if($node == null)
      return;

    $nodeList = XML::getNodesByXPath($node, '..');
    //return $node->parentNode;
    return isset($nodeList[0]) ? $nodeList[0]: NULL;
  }

  /**
   * Returns a string prefixed by the current language
   * @return string
   */
  public static function getAttrNameByLanguage($attrName)
  {
    $language = Config::$siteLanguage;
    return $language.'_'.$attrName;
  }

  /**
   * Returns a string prefixed by the default language
   * @return string
   */
  public static function getAttrNameByDefaultLanguage($attrName)
  {
    $language = Config::$defaultSiteLanguage;
    return $language.'_'.$attrName;
  }

  /**
   * Sets the language specific attribute value of a node
   * @return void
   */
  public static function setAttr( SimpleXMLElement $node, $attrName, $attrValue )
  {
    $node[self::getAttrNameByLanguage($attrName)] = $attrValue;
  }

  /**
   * Sets the non-language specific attribute value of a node
   * @return void
   */
  public static function setCommonAttr( SimpleXMLElement $node, $attrName, $attrValue )
  {
    $node[$attrName] = $attrValue;
  }

  /**
   * Add a new node to the SiteMap under the provided $parentNode param
   * NOTE: The changes will only be saved if you call the save function
   * @return bool
   */
  public static function add(SimpleXMLElement $parentNode, $controller, $function, $pageTitle='', $title='', $metaDesc='', $metaKeys='', $bannerURL='' )
  {
    $childNode = $parentNode->addChild('node');
    $childNode->addAttribute('controller', $controller);
    $childNode->addAttribute('function', $function);

    $childNode->addAttribute(self::getAttrNameByLanguage('pageTitle'), $pageTitle);
    $childNode->addAttribute(self::getAttrNameByLanguage('title'), $title);

    $sefTitle = trim(stripslashes(strip_tags($title)));
    $sefTitle = str_replace(array(' ','\'','"'),array('-', '', ''), $sefTitle);
    $sefTitle = strtolower($sefTitle);

    $childNode->addAttribute('sefTitle', $sefTitle);
    $childNode->addAttribute(self::getAttrNameByLanguage('metaDesc'), $metaDesc);
    $childNode->addAttribute(self::getAttrNameByLanguage('metaKeys'), $metaKeys);
    $childNode->addAttribute(self::getAttrNameByLanguage('bannerURL'), $bannerURL);

    return true;
  }

  /**
   * Add a new node to the SiteMap under the provided $parentNode param
   * NOTE: The changes will only be saved if you call the save function
   * @return bool
   */
  public static function addObject(SimpleXMLElement $parentNode, SimpleXMLElement $node)
  {
    $pageTitle = $node[self::getAttrNameByLanguage('pageTitle')];
    $title = $node[self::getAttrNameByLanguage('title')];
    $metaDesc = $node[self::getAttrNameByLanguage('metaDesc')];
    $metaKeys = $node[self::getAttrNameByLanguage('metaKeys')];
    $bannerURL = $node[self::getAttrNameByLanguage('bannerURL')];

    return self::add($parentNode, $node['controller'], $node['function'], $pageTitle, $title, $metaDesc, $metaKeys, $bannerURL);
  }

  /**
   * Save all changes made to the SiteMap
   * @return bool
   */
  public static function save()
  {
    return XML::save(self::$xmlObject, Config::$siteMapXML, true, 0777);
  }

  /**
   * Delete the node from the SiteMap
   * NOTE: The changes will only be saved if you call the save function
   * @return bool
   */
  public static function delete(SimpleXMLElement $node)
  {
    return XML::delete($node);
  }

  /**
   * Delete all nodes under the $parentNode
   * NOTE: The changes will only be saved if you call the save function
   * @return SimpleXMLElement
   */
  public static function deleteAllChilds(SimpleXMLElement $parentNode)
  {
    return XML::deleteChildsNodes($parentNode, 'node');
  }

  /**
   * Generate a HTML List based on the parameter values
   * @return string
   */
  public static function generateSiteMap( SimpleXMLElement $node, $depth = -1, $parentULId = '', $renderChildRoots = true, $showHomePageInMainMenu=false)
  {    
    return self::generate($node, $depth, $parentULId, $renderChildRoots,'', $showHomePageInMainMenu);
  }

  /**
   * Returns the Default Node
   * @return string
   */
  public static function getDefaultNode()
  {
    return self::$defaultNode;
  }

  public static function canDisplayNode($node)
  {
    if($node == NULL)
      return false;

    if($node->attributes()->controller == 'root')
      return false;

    if( ( is_object(Master::getCurrentController()->cms) ) && ( Master::getCurrentController()->cms->isAdminLoggedIn() ) && ( $node->attributes()->display == 'adminOnly' ))
      return true;

    if(( $node->attributes()->display == 'false' ) || ($node->attributes()->display == 'adminOnly'))
      return false;
    
    return true;
  }

  /**
   * Generate a HTML List based on the parameter values
   * @return string
   */
  public static function generateBreadCrumb( $node, $parentULId = '', $spacer = '<span class="spacer">></span>', $selectParent = true )
  {
    if($node == NULL)
      return;

    if($parentULId != '')
      $parentULId = ' id="'.trim($parentULId).'"';
    
    $html = '<ul'.$parentULId.' class="'.self::getCurrentDepthClass().'">';

    $nodes = array();

    while( self::canDisplayNode( $node ) )
    {
      $nodes[] = $node;
      $node = self::getParentNode($node);
    }        
    
    $nodes = array_reverse($nodes);

    foreach ($nodes as $key=>$node)
    {
      if($key == count($nodes)-1)
        $spacer = '';

      $html .= self::getLIforNode($node, $spacer, $selectParent, $key);
    }

    $html .= '</ul>';

    return $html;
  }

  public static function getAllAttrsByLanguage()
  {
    if(self::$currentNode == NULL)
      return array();

    $attrs = array();

    $attributes = self::$currentNode->attributes();
    foreach($attributes as $attr=>$value) {
      if( stristr($attr, Config::$defaultSiteLanguage.'_') )
      {
        list($lang, $attrName) = explode('_', $attr);
        $attrs[$attrName] = (string)$value;
      }

      if( stristr($attr, Config::$siteLanguage.'_') )
      {
        list($lang, $attrName) = explode('_', $attr);
        $attrs[$attrName] = (string)$value;
      }
    }

    return $attrs;
  }

  /**
   * Returns the current language specific attribute ( $attrName ) value of the $node parameter
   * @return string
   */
  public static function getAttr( SimpleXMLElement $node, $attrName, $selectParent = true )
  {
    $attrValue = '';
    $language = Config::$siteLanguage;

    if(($language != null) && ($language != '') && ( $node[self::getAttrNameByLanguage($attrName)] != '' ))
      $attrValue = $node[self::getAttrNameByLanguage($attrName)];
    else if( isset($node[self::getAttrNameByDefaultLanguage($attrName)]) )
      $attrValue = $node[self::getAttrNameByDefaultLanguage($attrName)];
    else if( isset($node[$attrName]) )
      $attrValue = $node[$attrName];
    else
      $attrValue = '';

    return trim($attrValue);
  }

  /**
   * Generates a HTML List based on the parameters given
   * @return string
   */
  public static function generate( $node, $depth = -1, $parentULId = '', $renderChildRoots = true, $spacer = '', $showHomePageInMainMenu=false)
  {
    if($node == NULL)
      return;

    self::$renderChildRoots = $renderChildRoots;
    self::$prevDepth = self::$depth = $depth;    

    return self::render($node, $parentULId, $spacer, $showHomePageInMainMenu);
  }

  /**
   * Check to see if the node has child nodes or not
   * @return bool
   */
  public static function hasChildren( SimpleXMLElement $node )
  {
    return ( count($node->children()) == 0) ? false : true ;
  }

  private static function getCurrentDepthClass()
  {
    return 'depth'.self::$depthCounter;
  }

  public static function getItemClassByNode(SimpleXMLElement $node)
  {
    return $node['function'];
  }

  /**
   * Generates and HTML List for the node given
   * @return string
   */
  public static function render( SimpleXMLElement $node, $parentULId = '', $spacer = '', $showHomePageInMainMenu=false )
  {
    $html = '';

    if( self::hasChildren($node) ) {
      if($parentULId !='')
        $html = '<ul id="'.$parentULId.'" class="'.self::getCurrentDepthClass().'">'."\n";
      else
        $html = '<ul>'."\n";

      $counter = 0;

      if( $showHomePageInMainMenu )
      {
        $html .= self::renderNode($node, $spacer, false, $counter);
        $counter++;
      }
      
      foreach ($node->children() as $childNode)
      {
        if(self::canDisplayNode($childNode))
        {
          $html .= self::renderNode($childNode, $spacer, true, $counter);
          self::$depthCounter = 0;
          $counter++;
        }
      }

      $html .= '</ul>'."\n";
    }
    return $html;
  }

  /**
   * Returns the URL to the node
   * @return string
   */
  public static function getURL( SimpleXMLElement $node )
  {
    $href = '';

    $siteURL = Config::$siteURL;

    if( Config::$appendIndexPhpToURL )
    {
      if( !stristr($siteURL, 'index.php/') )
        $siteURL = $siteURL.'index.php/';
    }

    if((string)$node->attributes()->url =='')
    {
      $uri = self::getAttr($node, 'sefTitle');

      if(self::getParentNode($node) == self::getRootNode())
        $href = $siteURL;

      if( self::canDisplayNode($node) )
        $href = $siteURL.$uri;

      while(($parentNode = self::getParentNode($node)) != self::getRootNode() )
      {
        if(($parentNode != self::getDefaultNode()) && (isset($parentNode['sefTitle'])) && ( self::canDisplayNode($parentNode) )  )
          $uri = self::getAttr($parentNode, 'sefTitle').'/'.$uri;

        $href = $siteURL.$uri;

        $node = self::getParentNode($node);
      }
    }
    else
      $href = $node->attributes()->url;
    
    return $href;
  }

  public static function isParentNodeOfCurrentNode($nodeToCheck)
  {
    $node =  self::getParentNode(self::getCurrentNode());

    while( self::canDisplayNode( $node ) && $node != self::getRootNode() )
    {
      if( $nodeToCheck == $node)
        return true;

      $node = self::getParentNode($node);
    }

    return false;
  }

  private static function getLIforNode(SimpleXMLElement $node, $spacer = '', $selectParent = true, $index='')
  {
    $html = '';

    $href = self::getURL($node);
    $title = self::getAttr($node, 'title', $selectParent);

    if($title == '')
      $title = $node['sefTitle'];

    $class = self::getAttr($node, 'cssClass', $selectParent);
    $target = self::getAttr($node, 'target', $selectParent);

    $class .= ' item'.$index.' '.self::getItemClassByNode($node);

    if( self::canDisplayNode($node) )
    {
      if(self::hasChildren($node))
        $class .= ' hasChildren';

      if($node == self::getCurrentNode())
        $class .= ' current';

      if(($selectParent) && (self::isParentNodeOfCurrentNode($node) ))
        $class .= ' parentSelect';

      if( trim($class) !='' )
        $class = ' class="'.trim($class).'"';

      if( trim($target) !='' )
        $target = ' target="'.trim($target).'"';

      $html = '<li'.$class.'><a href="'.$href.'"'.$target.'>'.$title.'</a>'.$spacer;
    }
    else
    {
      $class = trim($class);
      if( $class !='' )
        $html = '<li class="'.$class.'"><span>'.$title.'</span>';
      else
        $html = '<li><span>'.$title.'</span>';
    }

    return $html;
  }

  /**
   * Generates and HTML List for the node given
   * @return string
   */
  private static function renderNode( SimpleXMLElement $node, $spacer = '', $selectParent = true, $index='')
  {
    $customVariables = array();
    $customVariables['controller'] = (string)self::getAttr($node, 'controller', $selectParent);
    $customVariables['function'] = (string)self::getAttr($node, 'function', $selectParent);

    $spacer = Parser::parseData($spacer, $customVariables);

    if((string)$node->attributes()->display == 'false')
      return '';

    if((!self::$renderChildRoots) && ($node->attributes()->controller == 'root'))
      return '';
    
    $html = self::getLIforNode($node, $spacer, $selectParent, $index);
    
    if( self::hasChildren($node) )
    {
      if(( self::$depth ==-1 ) || ( (self::$depth - self::$depthCounter) > 0) )
      {
        self::$depthCounter++;

        $html .= "\n".'<ul class="'.self::getCurrentDepthClass().'">'."\n";

        $children = $node->children();

        $counter = 0;
        foreach ($children as $index=>$childNode)
        {
          $html .= self::renderNode($childNode, $spacer, $selectParent, $counter);
          $counter++;
        }
        
        $html .= '</ul>'."\n";

        self::$depthCounter--;
      }
    }

    $html .= '</li>'."\n";

    return $html;
  }

}
?>
